{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pipelines"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pipelines are an integral part of river. We encourage their usage and apply them in many of their examples.\n",
    "\n",
    "The `compose.Pipeline` contains all the logic for building and applying pipelines. A pipeline is essentially a list of estimators that are applied in sequence. The only requirement is that the first `n - 1` steps be transformers. The last step can be a regressor, a classifier, a clusterer, a transformer, etc. Here is an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:40.566014Z",
     "iopub.status.busy": "2020-11-25T21:24:40.565572Z",
     "iopub.status.idle": "2020-11-25T21:24:41.324285Z",
     "shell.execute_reply": "2020-11-25T21:24:41.324770Z"
    }
   },
   "outputs": [],
   "source": [
    "from river import compose\n",
    "from river import linear_model\n",
    "from river import preprocessing\n",
    "from river import feature_extraction\n",
    "\n",
    "model = compose.Pipeline(\n",
    "    preprocessing.StandardScaler(),\n",
    "    feature_extraction.PolynomialExtender(),\n",
    "    linear_model.LinearRegression()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also use the `|` operator, as so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.328607Z",
     "iopub.status.busy": "2020-11-25T21:24:41.328075Z",
     "iopub.status.idle": "2020-11-25T21:24:41.332148Z",
     "shell.execute_reply": "2020-11-25T21:24:41.331724Z"
    }
   },
   "outputs": [],
   "source": [
    "model = (\n",
    "    preprocessing.StandardScaler() |\n",
    "    feature_extraction.PolynomialExtender() |\n",
    "    linear_model.LinearRegression()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or, equally:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.336041Z",
     "iopub.status.busy": "2020-11-25T21:24:41.335078Z",
     "iopub.status.idle": "2020-11-25T21:24:41.336791Z",
     "shell.execute_reply": "2020-11-25T21:24:41.337167Z"
    }
   },
   "outputs": [],
   "source": [
    "model = preprocessing.StandardScaler() \n",
    "model |= feature_extraction.PolynomialExtender()\n",
    "model |= linear_model.LinearRegression()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A pipeline has a `draw` method that can be used to visualize it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.344724Z",
     "iopub.status.busy": "2020-11-25T21:24:41.344268Z",
     "iopub.status.idle": "2020-11-25T21:24:41.369389Z",
     "shell.execute_reply": "2020-11-25T21:24:41.368659Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.40.1 (20161225.0304)\n",
       " -->\n",
       "<!-- Title: %3 Pages: 1 -->\n",
       "<svg width=\"174pt\" height=\"332pt\"\n",
       " viewBox=\"0.00 0.00 174.39 332.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 328)\">\n",
       "<title>%3</title>\n",
       "<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-328 170.3852,-328 170.3852,4 -4,4\"/>\n",
       "<!-- x -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>x</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"83.1926\" cy=\"-306\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"83.1926\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">x</text>\n",
       "</g>\n",
       "<!-- StandardScaler -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>StandardScaler</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"83.1926\" cy=\"-234\" rx=\"63.8893\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"83.1926\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">StandardScaler</text>\n",
       "</g>\n",
       "<!-- x&#45;&gt;StandardScaler -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>x&#45;&gt;StandardScaler</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M83.1926,-287.8314C83.1926,-280.131 83.1926,-270.9743 83.1926,-262.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"86.6927,-262.4132 83.1926,-252.4133 79.6927,-262.4133 86.6927,-262.4132\"/>\n",
       "</g>\n",
       "<!-- PolynomialExtender -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>PolynomialExtender</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"83.1926\" cy=\"-162\" rx=\"83.3857\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"83.1926\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">PolynomialExtender</text>\n",
       "</g>\n",
       "<!-- StandardScaler&#45;&gt;PolynomialExtender -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>StandardScaler&#45;&gt;PolynomialExtender</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M83.1926,-215.8314C83.1926,-208.131 83.1926,-198.9743 83.1926,-190.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"86.6927,-190.4132 83.1926,-180.4133 79.6927,-190.4133 86.6927,-190.4132\"/>\n",
       "</g>\n",
       "<!-- LinearRegression -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>LinearRegression</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"83.1926\" cy=\"-90\" rx=\"72.5877\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"83.1926\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LinearRegression</text>\n",
       "</g>\n",
       "<!-- PolynomialExtender&#45;&gt;LinearRegression -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>PolynomialExtender&#45;&gt;LinearRegression</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M83.1926,-143.8314C83.1926,-136.131 83.1926,-126.9743 83.1926,-118.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"86.6927,-118.4132 83.1926,-108.4133 79.6927,-118.4133 86.6927,-118.4132\"/>\n",
       "</g>\n",
       "<!-- y -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>y</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"83.1926\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"83.1926\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">y</text>\n",
       "</g>\n",
       "<!-- LinearRegression&#45;&gt;y -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>LinearRegression&#45;&gt;y</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M83.1926,-71.8314C83.1926,-64.131 83.1926,-54.9743 83.1926,-46.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"86.6927,-46.4132 83.1926,-36.4133 79.6927,-46.4133 86.6927,-46.4132\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.dot.Digraph at 0x7f5a6b084310>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`compose.Pipeline` inherits from `base.Estimator`, which means that it has a `learn_one` method. You would expect `learn_one` to update each estimator, but **that's not actually what happens**. Instead, the transformers are updated when `predict_one` (or `predict_proba_one` for that matter) is called. Indeed, in online machine learning, we can update the unsupervised parts of our model when a sample arrives. We don't have to wait for the ground truth to arrive in order to update unsupervised estimators that don't depend on it. In other words, in a pipeline, `learn_one` updates the supervised parts, whilst `predict_one` updates the unsupervised parts. It's important to be aware of this behavior, as it is quite different to what is done in other libraries that rely on batch machine learning.\n",
    "\n",
    "Here is a small example to illustrate the previous point:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.374342Z",
     "iopub.status.busy": "2020-11-25T21:24:41.372850Z",
     "iopub.status.idle": "2020-11-25T21:24:41.377999Z",
     "shell.execute_reply": "2020-11-25T21:24:41.378355Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "({'ordinal_date': 736389,\n",
       "  'gallup': 43.843213,\n",
       "  'ipsos': 46.19925042857143,\n",
       "  'morning_consult': 48.318749,\n",
       "  'rasmussen': 44.104692,\n",
       "  'you_gov': 43.636914000000004},\n",
       " 43.75505)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from river import datasets\n",
    "\n",
    "dataset = datasets.TrumpApproval()\n",
    "x, y = next(iter(dataset))\n",
    "x, y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us call `predict_one`, which will update each transformer, but won't update the linear regression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.381884Z",
     "iopub.status.busy": "2020-11-25T21:24:41.381430Z",
     "iopub.status.idle": "2020-11-25T21:24:41.385124Z",
     "shell.execute_reply": "2020-11-25T21:24:41.384722Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.predict_one(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The prediction is nil because each weight of the linear regression is equal to 0."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.389126Z",
     "iopub.status.busy": "2020-11-25T21:24:41.388653Z",
     "iopub.status.idle": "2020-11-25T21:24:41.390942Z",
     "shell.execute_reply": "2020-11-25T21:24:41.391322Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "defaultdict(float,\n",
       "            {'ordinal_date': 736389.0,\n",
       "             'gallup': 43.843213,\n",
       "             'ipsos': 46.19925042857143,\n",
       "             'morning_consult': 48.318749,\n",
       "             'rasmussen': 44.104692,\n",
       "             'you_gov': 43.636914000000004})"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model['StandardScaler'].means"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we can see, the means of each feature have been updated, even though we called `predict_one` and not `learn_one`.\n",
    "\n",
    "Note that if you call `transform_one` with a pipeline who's last step is not a transformer, then the output from the last transformer (which is thus the penultimate step) will be returned:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.395827Z",
     "iopub.status.busy": "2020-11-25T21:24:41.395327Z",
     "iopub.status.idle": "2020-11-25T21:24:41.397685Z",
     "shell.execute_reply": "2020-11-25T21:24:41.398052Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'ordinal_date': 0.0,\n",
       " 'gallup': 0.0,\n",
       " 'ipsos': 0.0,\n",
       " 'morning_consult': 0.0,\n",
       " 'rasmussen': 0.0,\n",
       " 'you_gov': 0.0,\n",
       " 'ordinal_date*ordinal_date': 0.0,\n",
       " 'gallup*ordinal_date': 0.0,\n",
       " 'ipsos*ordinal_date': 0.0,\n",
       " 'morning_consult*ordinal_date': 0.0,\n",
       " 'ordinal_date*rasmussen': 0.0,\n",
       " 'ordinal_date*you_gov': 0.0,\n",
       " 'gallup*gallup': 0.0,\n",
       " 'gallup*ipsos': 0.0,\n",
       " 'gallup*morning_consult': 0.0,\n",
       " 'gallup*rasmussen': 0.0,\n",
       " 'gallup*you_gov': 0.0,\n",
       " 'ipsos*ipsos': 0.0,\n",
       " 'ipsos*morning_consult': 0.0,\n",
       " 'ipsos*rasmussen': 0.0,\n",
       " 'ipsos*you_gov': 0.0,\n",
       " 'morning_consult*morning_consult': 0.0,\n",
       " 'morning_consult*rasmussen': 0.0,\n",
       " 'morning_consult*you_gov': 0.0,\n",
       " 'rasmussen*rasmussen': 0.0,\n",
       " 'rasmussen*you_gov': 0.0,\n",
       " 'you_gov*you_gov': 0.0}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.transform_one(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In many cases, you might want to connect a step to multiple steps. For instance, you might to extract different kinds of features from a single input. An elegant way to do this is to use a `compose.TransformerUnion`. Essentially, the latter is a list of transformers who's results will be merged into a single `dict` when `transform_one` is called. As an example let's say that we want to apply a `feature_extraction.RBFSampler` as well as the `feature_extraction.PolynomialExtender`. This may be done as so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.405229Z",
     "iopub.status.busy": "2020-11-25T21:24:41.401466Z",
     "iopub.status.idle": "2020-11-25T21:24:41.423369Z",
     "shell.execute_reply": "2020-11-25T21:24:41.422983Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.40.1 (20161225.0304)\n",
       " -->\n",
       "<!-- Title: %3 Pages: 1 -->\n",
       "<svg width=\"306pt\" height=\"332pt\"\n",
       " viewBox=\"0.00 0.00 306.39 332.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 328)\">\n",
       "<title>%3</title>\n",
       "<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-328 302.3875,-328 302.3875,4 -4,4\"/>\n",
       "<!-- x -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>x</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"162.1926\" cy=\"-306\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"162.1926\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">x</text>\n",
       "</g>\n",
       "<!-- StandardScaler -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>StandardScaler</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"162.1926\" cy=\"-234\" rx=\"63.8893\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"162.1926\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">StandardScaler</text>\n",
       "</g>\n",
       "<!-- x&#45;&gt;StandardScaler -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>x&#45;&gt;StandardScaler</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M162.1926,-287.8314C162.1926,-280.131 162.1926,-270.9743 162.1926,-262.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"165.6927,-262.4132 162.1926,-252.4133 158.6927,-262.4133 165.6927,-262.4132\"/>\n",
       "</g>\n",
       "<!-- PolynomialExtender -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>PolynomialExtender</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"83.1926\" cy=\"-162\" rx=\"83.3857\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"83.1926\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">PolynomialExtender</text>\n",
       "</g>\n",
       "<!-- StandardScaler&#45;&gt;PolynomialExtender -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>StandardScaler&#45;&gt;PolynomialExtender</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M143.0689,-216.5708C133.1476,-207.5286 120.8645,-196.3338 110.0106,-186.4417\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"112.1626,-183.6675 102.414,-179.5182 107.4473,-188.8411 112.1626,-183.6675\"/>\n",
       "</g>\n",
       "<!-- RBFSampler -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>RBFSampler</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"241.1926\" cy=\"-162\" rx=\"57.3905\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"241.1926\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">RBFSampler</text>\n",
       "</g>\n",
       "<!-- StandardScaler&#45;&gt;RBFSampler -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>StandardScaler&#45;&gt;RBFSampler</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M181.3163,-216.5708C191.3846,-207.3946 203.8852,-196.0017 214.8561,-186.0029\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"217.4843,-188.3431 222.5176,-179.0202 212.7691,-183.1695 217.4843,-188.3431\"/>\n",
       "</g>\n",
       "<!-- LinearRegression -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>LinearRegression</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"162.1926\" cy=\"-90\" rx=\"72.5877\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"162.1926\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LinearRegression</text>\n",
       "</g>\n",
       "<!-- PolynomialExtender&#45;&gt;LinearRegression -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>PolynomialExtender&#45;&gt;LinearRegression</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M102.7207,-144.2022C112.5744,-135.2217 124.6783,-124.1902 135.3887,-114.4289\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"137.8558,-116.9159 142.8892,-107.593 133.1406,-111.7423 137.8558,-116.9159\"/>\n",
       "</g>\n",
       "<!-- RBFSampler&#45;&gt;LinearRegression -->\n",
       "<g id=\"edge5\" class=\"edge\">\n",
       "<title>RBFSampler&#45;&gt;LinearRegression</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M222.4707,-144.937C212.484,-135.8352 200.02,-124.4756 189.0218,-114.4519\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"191.0775,-111.5899 181.3289,-107.4407 186.3622,-116.7636 191.0775,-111.5899\"/>\n",
       "</g>\n",
       "<!-- y -->\n",
       "<g id=\"node6\" class=\"node\">\n",
       "<title>y</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"162.1926\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"162.1926\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">y</text>\n",
       "</g>\n",
       "<!-- LinearRegression&#45;&gt;y -->\n",
       "<g id=\"edge6\" class=\"edge\">\n",
       "<title>LinearRegression&#45;&gt;y</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M162.1926,-71.8314C162.1926,-64.131 162.1926,-54.9743 162.1926,-46.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"165.6927,-46.4132 162.1926,-36.4133 158.6927,-46.4133 165.6927,-46.4132\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.dot.Digraph at 0x7f5a6b0843a0>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = (\n",
    "    preprocessing.StandardScaler() |\n",
    "    (feature_extraction.PolynomialExtender() + feature_extraction.RBFSampler()) |\n",
    "    linear_model.LinearRegression()\n",
    ")\n",
    "\n",
    "model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the `+` symbol acts as a shorthand notation for creating a `compose.TransformerUnion`, which means that we could have declared the above pipeline as so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:24:41.427476Z",
     "iopub.status.busy": "2020-11-25T21:24:41.427021Z",
     "iopub.status.idle": "2020-11-25T21:24:41.430401Z",
     "shell.execute_reply": "2020-11-25T21:24:41.430003Z"
    }
   },
   "outputs": [],
   "source": [
    "model = (\n",
    "    preprocessing.StandardScaler() |\n",
    "    compose.TransformerUnion(\n",
    "        feature_extraction.PolynomialExtender(),\n",
    "        feature_extraction.RBFSampler()\n",
    "    ) |\n",
    "    linear_model.LinearRegression()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pipelines provide the benefit of removing a lot of cruft by taking care of tedious details for you. They also enable to clearly define what steps your model is made of. Finally, having your model in a single object means that you can move it around more easily. Note that you can include user-defined functions in a pipeline by using a `compose.FuncTransformer`."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
